#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pd_linux.h"
#include "config_cdc.h"
#include "libusb.h"
#include "xlibusb.h"

namespace config_cdc
{

    const int CDC_REQTYPE_HOST2DEVICE = 0x21;
    const int CDC_REQTYPE_DEVICE2HOST = 0xA1;

    const int CDC_SET_LINE_CODING = 0x20;
    const int CDC_GET_LINE_CODING = 0x21;
    const int CDC_SET_CONTROL_LINE_STATE = 0x22;

    const int CDC_SET_CONTROL_LINE_STATE_RTS = 0x2;
    const int CDC_SET_CONTROL_LINE_STATE_DTR = 0x1;

    const int DATA_BITS_5 = 5;
    const int DATA_BITS_6 = 6;
    const int DATA_BITS_7 = 7;
    const int DATA_BITS_8 = 8;

    const int STOP_BITS_1 = 1;
    const int STOP_BITS_15 = 3;
    const int STOP_BITS_2 = 2;

    const int PARITY_NONE = 0;
    const int PARITY_ODD = 1;
    const int PARITY_EVEN = 2;
    const int PARITY_MARK = 3;
    const int PARITY_SPACE = 4;

    const int FLOW_CONTROL_OFF = 0;
    const int FLOW_CONTROL_RTS_CTS = 1;
    const int FLOW_CONTROL_DSR_DTR = 2;
    const int FLOW_CONTROL_XON_XOFF = 3;
    /***
     *  Default Serial Configuration
     *  Baud rate: 115200
     *  Data bits: 8
     *  Stop bits: 1
     *  Parity: None
     *  Flow Control: Off
     */
    unsigned char CDC_DEFAULT_LINE_CODING[]
    {
        (unsigned char) 0x00, // Offset 0:4 dwDTERate
        (unsigned char) 0xC2,
        (unsigned char) 0x01,
        (unsigned char) 0x00,
        (unsigned char) 0x00, // Offset 5 bCharFormat (1 Stop bit)
        (unsigned char) 0x00, // bParityType (None)
        (unsigned char) 0x08  // bDataBits (8)
    };

    const int CDC_CONTROL_LINE_ON = 0x0003;
    const int CDC_CONTROL_LINE_OFF = 0x0000;

    int setControlCommand(void *handle, int request, int value, unsigned char *data, int dataLength)
    {
        int response = x_libusb_controltransfer(handle, CDC_REQTYPE_HOST2DEVICE, request, value, 0, data, dataLength, 1000);
        return response;
    }

    int getLineCoding(void *handle, unsigned char *data, int length)
    {
        int response = x_libusb_controltransfer(handle, CDC_REQTYPE_DEVICE2HOST, CDC_GET_LINE_CODING, 0, 0, data, length, 1000);
        return response;
    }
    int  Init(void *handle)
    {
        setControlCommand(handle, CDC_SET_LINE_CODING, 0, CDC_DEFAULT_LINE_CODING, 7);
        setControlCommand(handle, CDC_SET_CONTROL_LINE_STATE, CDC_CONTROL_LINE_ON, NULL, 0);
        return 0;
    }

    void  Free(void *handle)
    {
        setControlCommand(handle, CDC_SET_CONTROL_LINE_STATE, CDC_CONTROL_LINE_OFF, NULL, 0);
    }

    void SetBaudRate(void *handle, int baudRate)
    {
        unsigned char data[7] = {0x0};
        getLineCoding(handle, data, 7);
        data[0] = (unsigned char)(baudRate & 0xff);
        data[1] = (unsigned char)(baudRate >> 8 & 0xff);
        data[2] = (unsigned char)(baudRate >> 16 & 0xff);
        data[3] = (unsigned char)(baudRate >> 24 & 0xff);
        setControlCommand(handle, CDC_SET_LINE_CODING, 0, data, 7);
    }

    void SetParity(void *handle, int parity)
    {
        unsigned char data[7] = {0x0};
        getLineCoding(handle, data, 7);
        switch(parity)
        {
            case PARITY_NONE:
                data[5] = 0x00;
                break;
            case PARITY_ODD:
                data[5] = 0x01;
                break;
            case PARITY_EVEN:
                data[5] = 0x02;
                break;
            case PARITY_MARK:
                data[5] = 0x03;
                break;
            case PARITY_SPACE:
                data[5] = 0x04;
                break;
            default:
                return;
        }

        setControlCommand(handle, CDC_SET_LINE_CODING, 0, data, 7);
    }

    void SetFlowControl(void *handle, int flowControl)
    {
        //resverd
    }

    void SetDataBits(void *handle, int dataBits)
    {
        unsigned char data[7] = {0x0};
        getLineCoding(handle, data, 7);
        switch(dataBits)
        {
            case DATA_BITS_5:
                data[6] = 0x05;
                break;
            case DATA_BITS_6:
                data[6] = 0x06;
                break;
            case DATA_BITS_7:
                data[6] = 0x07;
                break;
            case DATA_BITS_8:
                data[6] = 0x08;
                break;
            default:
                return;
        }

        setControlCommand(handle, CDC_SET_LINE_CODING, 0, data, 7);
    }

    void SetStopBits(void *handle, int stopBits)
    {
        unsigned char data[7] = {0x0};
        getLineCoding(handle, data, 7);
        switch(stopBits)
        {
            case STOP_BITS_1:
                data[4] = 0x00;
                break;
            case STOP_BITS_15:
                data[4] = 0x01;
                break;
            case STOP_BITS_2:
                data[4] = 0x02;
                break;
            default:
                return;
        }

        setControlCommand(handle, CDC_SET_LINE_CODING, 0, data, 7);
    }
    void SetBreak(void *handle, int state)
    {
        //resverd
    }
};

